/*
 * Copyright 2008 Andrej Herich
 * 
 * http://azetclient.yw.sk/
 * http://azetclient.googlecode.com/
 * 
 * This file is part of AzetClient.
 * 
 * AzetClient is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * AzetClient is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with AzetClient.  If not, see <http://www.gnu.org/licenses/>.
 */

package sk.yw.azetclient;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.Proxy;
import java.text.Collator;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.ResourceBundle;
import javax.swing.SwingUtilities;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import sk.yw.azetclient.gui.MainFrame;
import sk.yw.azetclient.preferences.Preferences;

/**
 *
 * @author error216
 */
public class Main {
    
    public static final String APPLICATION_NAME = "AzetClient";
    public static final String VERSION = "0.0alpha1";
    
    public static final Locale SLOVAK_LOCALE = new Locale("sk", "SK");
    
    public static final Collator SLOVAK_COLLATOR =
            Collator.getInstance(SLOVAK_LOCALE);
    
    public static final String CONFIG_DIR =
            System.getProperty("user.home")
            + System.getProperty("file.separator")
            + ".azetclient";
    private static final String MAIN_PROPERTIES_FILE_NAME = "main.properties";
    private static final String MAIN_PROPERTIES_SYSTEM_RESOURCE_PATH
            = "sk/yw/azetclient/main.properties";
    
    private static String customPropertiesFileName;
    
    private static Properties mainProperties;
    private static ResourceBundle textsBundle =
	    ResourceBundle.getBundle("sk.yw.azetclient.texts");
    
    private static Preferences preferences = new Preferences();
    
    private static final Logger logger = Logger.getLogger(Main.class);
    
    private static Proxy proxy;
    
    private static MainFrame mainFrame;

    /**
     * Main method.
     * 
     * @param args the command line arguments
     */
    public static void main(String[] args) throws Exception {
	createConfigDir();
        customPropertiesFileName = (args.length > 0) ? args[1] : null;
        loadProperties(customPropertiesFileName);
	
	PropertyConfigurator.configure(mainProperties);
        
	logger.info(APPLICATION_NAME + " " + VERSION + " start");
        logger.info("Running on " + System.getProperty("os.name") + " "
                + System.getProperty("os.version") + " "
                + System.getProperty("os.arch"));
        logger.info("Java: " + System.getProperty("java.vendor") + " "
                + System.getProperty("java.version"));
	try {
            createProxy();
	    CookieHandler.setDefault(new CookieManager());
            
            SwingUtilities.invokeLater(new Runnable(){
                public void run() {
                    try {
                        mainFrame = new MainFrame();
                        getMainFrame().setVisible(true);
                        getMainFrame().start();
                    } catch (Throwable throwable) {
                        logger.fatal("Unexpected exception occurred",
                                throwable);
                        System.exit(0);
                    }
                }
            });
	} catch (Throwable throwable) {
	    logger.fatal("Unexpected exception occurred", throwable);
	}
    }
    
    private static void createConfigDir() {
        File configDir = new File(CONFIG_DIR);
        try {
            if (!configDir.exists()) {
                if (!configDir.mkdir()) {
                    BasicConfigurator.configure();
                    logger.fatal("Unable to create configuration directory.");
                }
            }
        } catch (SecurityException ex) {
            BasicConfigurator.configure();
            logger.fatal("Security exception occured while trying to create configuration directory.", ex);
        } catch (Exception ex) {
            BasicConfigurator.configure();
            logger.fatal("Unexpected exception occured while trying to create configuration directory.", ex);
        }

    }
    
    public static void loadProperties(String fileName) throws Exception {
        if (fileName == null) {
            fileName = CONFIG_DIR
                    + File.separator + MAIN_PROPERTIES_FILE_NAME;
        }
	try {
            Properties defaultProperties = new Properties();
            ClassLoader classLoader = Main.class.getClassLoader();
	    defaultProperties.load(classLoader
		    .getResource(MAIN_PROPERTIES_SYSTEM_RESOURCE_PATH)
		    .openStream());
            mainProperties = new Properties(defaultProperties);
            
            File customPropFile = new File(fileName);
            if (customPropFile.exists()) {
                mainProperties.load(new FileReader(customPropFile));
            }
	} catch (Exception ex) {
	    BasicConfigurator.configure();
	    logger.fatal("Unable to load properties.", ex);
            throw ex;
	}
    }
    
    public synchronized static void saveProperties() {
        logger.info("Saving properties.");
        if (customPropertiesFileName != null) {
            logger.info("Custom properties file used; not saving properties.");
            return;
        }
        
        synchronized (mainProperties) {
            try {
                BufferedWriter writer = new BufferedWriter(new FileWriter(
                        CONFIG_DIR + File.separator
                        + MAIN_PROPERTIES_FILE_NAME));
                mainProperties.store(writer, APPLICATION_NAME + " " + VERSION
                        + " properties");
                writer.close();
            } catch (IOException ex) {
                logger.error("Unable to save properties.", ex);
            }
        }
    }
    
    public static String getProperty(String key) {
	synchronized (mainProperties) {
            return mainProperties.getProperty(key);
        }
    }
    
    public static void setProperty(String key, String value) {
        synchronized (mainProperties) {
            mainProperties.setProperty(key, value);
        }
    }
    
    public static String getText(String key) {
	return textsBundle.getString(key);
    }
    
    /**
     * Creates proxy from properties.
     */
    private static void createProxy() {
        if (getProperty("azetclient.proxy.type") == null) {
            proxy = Proxy.NO_PROXY;
            return;
        }
        
        // azetclient.proxy.type property set, load proxy settings
        try {
            Proxy.Type type = Proxy.Type.valueOf(
                    getProperty("azetclient.proxy.type"));
            String hostname = getProperty("azetclient.proxy.hostname");
            int port = Integer.parseInt(getProperty("azetclient.proxy.port"));
            
            proxy = new Proxy(type,
                    new java.net.InetSocketAddress(hostname, port));
        } catch (Exception ex) {
            logger.error("Unable to load proxy settings, using no proxy.", ex);
            proxy = Proxy.NO_PROXY;
        }
    }
    
    public static Proxy getProxy() {
	return proxy;
    }

    public static MainFrame getMainFrame() {
        return mainFrame;
    }
    
    public static void addPreference(String name, String initalValue,
            sk.yw.azetclient.preferences.Handler handler) {
        preferences.add(name, initalValue, handler);
    }
    
    public static void setPreferenceValue(String name, String newValue) {
        preferences.setValue(name, newValue);
    }
    
    public static void applyPreferences() {
        Map<String,String> changed = preferences.getChanged();
        preferences.apply();
        
        if (changed.isEmpty()) return;
        
        for (String key : changed.keySet()) {
            setProperty(key, changed.get(key));
        }
        
        new Thread() {
            @Override
            public void run() {
                saveProperties();
            }
        }.start();
    }

}
